package com.jiangyg.mall.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jiangyg.mall.admin.constant.ColumnConstant;
import com.jiangyg.mall.admin.dao.*;
import com.jiangyg.mall.admin.dto.UserDTO;
import com.jiangyg.mall.admin.dto.UserResourceDTO;
import com.jiangyg.mall.admin.entity.*;
import com.jiangyg.mall.admin.enums.PermissionType;
import com.jiangyg.mall.admin.vo.UserAuthzInfoVO;
import com.jiangyg.mall.admin.vo.UserVO;
import com.jiangyg.mall.core.support.env.EnvConstant;
import com.jiangyg.mall.core.enums.Enable;
import com.jiangyg.mall.core.exception.BusinessException;
import com.jiangyg.mall.core.support.restful.Pagination;
import com.jiangyg.mall.core.support.snowflake.SnowflakeBuilder;
import com.jiangyg.mall.core.utils.I18n;
import com.jiangyg.mall.core.utils.bean.BeanUtils;
import com.jiangyg.mall.db.mybatis.BaseServiceImpl;
import com.jiangyg.mall.db.mybatis.metadata.Pager;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends BaseServiceImpl<UserDao, User> implements UserService, BeanFactoryAware {

    private BeanFactory beanFactory;

    @Autowired
    public UserServiceImpl(UserDao baseMapper) {
        super(baseMapper);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public Pagination<UserVO> selectPagination(Pagination<UserVO> pagination, boolean simple) throws Exception {
        // 1. 分页查询用户信息
        {
            // 1.1 构建动态查询条件
            final QueryWrapper<User> wrapper = this.buildQueryWrapper(pagination, User.class);
            // 1.2 拼接自定义查询条件
            final String content = MapUtils.getString(pagination.toSearchMap(), "content");
            if (StringUtils.isNotBlank(content)) {
                wrapper.and(w -> w.like(ColumnConstant.ID, content).or().like(ColumnConstant.FULLNAME, content)
                        .or().like(ColumnConstant.USERNAME, content).or().like(ColumnConstant.PHONE, content)
                        .or().like(ColumnConstant.MAIL, content));
            }
            // 1.3 执行分页查询
            Pager<User> pager = Pager.from(pagination);
            pager = this.baseMapper.selectPage(pager, wrapper);
            pagination.setRecords(BeanUtils.transformList(pager.getRecords(), UserVO.class));
            pagination.setTotal(pager.getTotal());
            // 1.4 简单查询或者没有查询到数据则直接返回
            if (simple || CollectionUtils.isEmpty(pagination.getRecords())) {
                return pagination;
            }
        }
        // 2. 本次查询的所有用户ID节后
        final List<UserVO> users = pagination.getRecords();
        final List<Long> userIds =
                users.stream()
                        .map(UserVO::getId)
                        .map(Long::parseLong)
                        .collect(Collectors.toList());
        // 3. 查询用户列表的部门信息
        {
            // 3.1 根据用户ID查询所有用户部门关联信息
            final DepartUserDao departUserDao = this.beanFactory.getBean(DepartUserDao.class);
            final QueryWrapper<DepartUser> wrapper = new QueryWrapper<>();
            wrapper.in(ColumnConstant.USER_ID, userIds);
            final List<DepartUser> departUserList = departUserDao.selectList(wrapper);
            // TODO 以后考虑用户部门也支持一对多
            // 3.2 只有用户和部门存在关联，才进行此操作，尽可能查询减少操作
            if (CollectionUtils.isNotEmpty(departUserList)) {
                // 3.2.1 根据关联出来的部门ID，查询所有部门信息
                final DepartDao departDao = this.beanFactory.getBean(DepartDao.class);
                final QueryWrapper<Depart> dwrapper = new QueryWrapper<>();
                final List<Long> departIds = departUserList.stream().map(DepartUser::getDepartId).distinct().collect(Collectors.toList());
                dwrapper.in(ColumnConstant.ID, departIds);
                final List<Depart> departList = departDao.selectList(dwrapper);
                // 3.2.2 根据查询出来的关联关系，反写用户的部门信息
                users.forEach(user -> {
                    final Long userId = Long.parseLong(user.getId());
                    departUserList.stream().filter(du -> du.getUserId().equals(userId)).findAny().ifPresent(du -> {
                        user.setDepartId(String.valueOf(du.getDepartId()));
                        departList.stream().filter(d -> d.getId().equals(du.getDepartId())).findAny().ifPresent(d -> user.setDepartName(d.getName()));
                    });
                });
            }
        }
        // 4. 查询用户列表的角色信息
        // 4.1 查询用户角色信息
        final RoleUserDao roleUserDao = this.beanFactory.getBean(RoleUserDao.class);
        final QueryWrapper<RoleUser> wrapper = new QueryWrapper<>();
        wrapper.in(ColumnConstant.USER_ID, userIds);
        final List<RoleUser> roleUserList = roleUserDao.selectList(wrapper);
        // 4.2 只有用户和部门存在关联，才进行此操作，尽可能查询减少操作
        if (CollectionUtils.isNotEmpty(roleUserList)) {
            // 4.2.1 根据关联出来的角色ID，查询所有角色信息
            final RoleDao roleDao = this.beanFactory.getBean(RoleDao.class);
            final List<Long> roleIds = roleUserList.stream().map(RoleUser::getRoleId).distinct().collect(Collectors.toList());
            final QueryWrapper<Role> rwrapper = new QueryWrapper<>();
            rwrapper.in(ColumnConstant.ID, roleIds);
            final List<Role> roleList = roleDao.selectList(rwrapper);
            // 4.2.2 根据查询出来的关联关系，反写用户的角色信息
            users.forEach(user -> {
                final Long userId = Long.parseLong(user.getId());
                final List<RoleUser> roleUsers = roleUserList.stream().filter(ru -> ru.getUserId().equals(userId)).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(roleUsers)) {
                    return;
                }
                final List<String> userRoleIds = roleUsers.stream().map(RoleUser::getRoleId).map(String::valueOf).collect(Collectors.toList());
                user.setRoleIds(userRoleIds);
                final List<Role> roles = roleList.stream().filter(r -> roleUsers.stream().anyMatch(ru -> ru.getRoleId().equals(r.getId()))).collect(Collectors.toList());
                final String userRoleNames = roles.stream().map(Role::getName).collect(Collectors.joining(","));
                user.setRoleNames(userRoleNames);
            });
        }
        return pagination;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdate(UserDTO dto) throws Exception {
        // 判断是否loginName是否重复
        final Long userId = dto.getId();
        if (this.baseMapper.selectCountByUsername(userId, dto.getUsername()) > 0) {
            throw new BusinessException(I18n.getString("mall.admin.user.add.exist"));
        }
        final User entity = new User();
        BeanUtils.copyProperties(entity, dto);
        if (userId != null) {
            // 修改用户信息
            this.baseMapper.updateById(entity);
        } else {
            // 新增用户信息
            final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            final String defaultPassword = EnvConstant.getString("admin.user.default-password");
            entity.setPassword(encoder.encode(defaultPassword));
            entity.setId(SnowflakeBuilder.nextId());
            this.baseMapper.insert(entity);
        }
        // 部门用户关系保存
        this.saveDepartUserRelation(entity, dto.getDepartId());
        // 保存角色用户关系
        this.saveRoleUserRelation(entity, dto.getRoleIds());
        return true;
    }

    /**
     * 功能描述：保存角色用户关系
     *
     * @param user 用户信息
     */
    private void saveRoleUserRelation(User user, List<Long> roleIds) {
        final RoleUserDao roleUserDao = this.beanFactory.getBean(RoleUserDao.class);
        // 如果角色列表为空，则删除
        if (CollectionUtils.isEmpty(roleIds)) {
            final UpdateWrapper<RoleUser> wrapper = new UpdateWrapper<>();
            wrapper.eq(ColumnConstant.USER_ID, user.getId());
            roleUserDao.delete(wrapper);
            return;
        }
        // 查询关系是否存在，存着则先删后增
        final QueryWrapper<RoleUser> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.USER_ID, user.getId());
        final Integer count = roleUserDao.selectCount(wrapper);
        if (count > 0) {
            roleUserDao.delete(wrapper);
        }
        roleIds.forEach(roleId -> roleUserDao.insert(new RoleUser(roleId, user.getId())));
    }

    /**
     * 功能描述：部门用户关系保存
     *
     * @param user 用户信息
     */
    private void saveDepartUserRelation(User user, Long departId) {
        final DepartUserDao departUserDao = this.beanFactory.getBean(DepartUserDao.class);
        // 如果部门为空，则表示删除
        if (departId == null) {
            final UpdateWrapper<DepartUser> wrapper = new UpdateWrapper<>();
            wrapper.eq(ColumnConstant.USER_ID, user.getId());
            departUserDao.delete(wrapper);
            return;
        }
        // 查询关系是否存在，不存在则新增，存在则先删后增
        final QueryWrapper<DepartUser> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.USER_ID, user.getId());
        final Integer count = departUserDao.selectCount(wrapper);
        if (count > 0) {
            departUserDao.delete(wrapper);
        }
        departUserDao.insert(new DepartUser(departId, user.getId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean savePermission(UserResourceDTO userResourceDTO) {
        // TODO 判断保存的ID是否真正有权限
        final PermissionDao permissionDao = this.beanFactory.getBean(PermissionDao.class);
        // 用户新的资源列表
        final List<Long> resourceIds = userResourceDTO.getResourceIds();
        // 如果资源列表为空，说明为删除处理
        if (CollectionUtils.isNotEmpty(resourceIds)) {
            final UpdateWrapper<Permission> wrapper = new UpdateWrapper<>();
            wrapper.eq(ColumnConstant.REFERENCE_ID, userResourceDTO.getUserId());
            wrapper.eq(ColumnConstant.TYPE, PermissionType.U.code);
            permissionDao.delete(wrapper);
            return true;
        }
        // 先查询已有的用户资源权限
        final QueryWrapper<Permission> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.REFERENCE_ID, userResourceDTO.getUserId());
        wrapper.eq(ColumnConstant.TYPE, PermissionType.U.code);
        final List<Permission> permissions = permissionDao.selectList(wrapper);
        // 用户已有资源列表
        final List<Long> exitsResourceIds = permissions.stream().
                map(Permission::getResourceId).collect(Collectors.toList());
        // 本次需要删除、修改的资源列表
        final List<Long> batchDeleteIds = new ArrayList<>();
        exitsResourceIds.forEach(resourceId -> {
            if (!resourceIds.contains(resourceId)) {
                batchDeleteIds.add(resourceId);
            }
        });
        final List<Long> batchInsertIds = resourceIds.stream().
                filter(exitsResourceIds::contains).collect(Collectors.toList());
        // 批量修改已存在权限为未删除标志
        final Permission permission = new Permission();
        permission.setEnable(Enable.Y.scode);
        permissionDao.update(permission, wrapper);
        // 批量删除
        permissionDao.deleteBatchIds(batchDeleteIds);
        // 新增
        resourceIds.forEach(resourceId -> {
            final Permission perm = new Permission();
            perm.setReferenceId(userResourceDTO.getUserId());
            perm.setReferenceId(resourceId);
            perm.setType(PermissionType.U.code);
            permissionDao.insert(perm);
        });
        return true;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public UserDTO selectById(Long userId) {
        return this.baseMapper.selectUserById(userId);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<String> selectRoleIdsById(Long userId) {
        final RoleUserDao roleUserDao = this.beanFactory.getBean(RoleUserDao.class);
        final QueryWrapper<RoleUser> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.USER_ID, userId);
        final List<RoleUser> roleUsers = roleUserDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(roleUsers)) {
            return null;
        }
        return roleUsers.stream()
                .map(ru -> ObjectUtils.toString(ru.getRoleId()))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return false;
        }
        final DepartUserDao departUserDao = this.beanFactory.getBean(DepartUserDao.class);
        final RoleUserDao roleUserDao = this.beanFactory.getBean(RoleUserDao.class);
        final PermissionDao permissionDao = this.beanFactory.getBean(PermissionDao.class);
        // 需要先解除用户-部门关联关系
        final QueryWrapper<DepartUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.in(ColumnConstant.USER_ID, userIds);
        final Integer count = departUserDao.selectCount(queryWrapper);
        if (count > 0) {
            throw BusinessException.instance("mall.admin.user.delete.hasDepartRelation");
        }
        // 删除角色用户关联
        final UpdateWrapper<RoleUser> deleteRUWrapper = new UpdateWrapper<>();
        deleteRUWrapper.in(ColumnConstant.USER_ID, userIds);
        roleUserDao.delete(deleteRUWrapper);
        // 删除用户权限
        final UpdateWrapper<Permission> deletePwrapper = new UpdateWrapper<>();
        deletePwrapper.in(ColumnConstant.REFERENCE_ID, userIds);
        permissionDao.delete(deletePwrapper);
        // 删除用户信息
        return this.baseMapper.deleteBatchIds(userIds) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public UserAuthzInfoVO selectAuthzInfoByAccount(String account) throws Exception {
        final QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnConstant.USERNAME, account).or().eq(ColumnConstant.PHONE, account);
        final User user = this.baseMapper.selectOne(wrapper);
        if (user == null) {
            return null;
        }
        return BeanUtils.transform(user, UserAuthzInfoVO.class);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

}
